package org.droidplanner.android.maps.providers.google_map; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Point; import android.location.Location; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.FragmentActivity; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import com.google.android.gms.location.LocationSettingsRequest; import com.google.android.gms.location.LocationSettingsResult; import com.google.android.gms.location.LocationSettingsStates; import com.google.android.gms.location.LocationSettingsStatusCodes; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.MapsInitializer; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.Projection; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.UiSettings; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.model.Polygon; import com.google.android.gms.maps.model.PolygonOptions; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.PolylineOptions; import com.google.android.gms.maps.model.TileOverlay; import com.google.android.gms.maps.model.TileOverlayOptions; import com.o3dr.android.client.Drone; import com.o3dr.services.android.lib.coordinate.LatLong; import com.o3dr.services.android.lib.drone.attribute.AttributeEvent; import com.o3dr.services.android.lib.drone.attribute.AttributeType; import com.o3dr.services.android.lib.drone.property.FootPrint; import com.o3dr.services.android.lib.drone.property.Gps; import com.o3dr.services.android.lib.util.googleApi.GoogleApiClientManager; import com.o3dr.services.android.lib.util.googleApi.GoogleApiClientManager.GoogleApiClientTask; import org.droidplanner.android.DroidPlannerApp; import org.droidplanner.android.R; import org.droidplanner.android.fragments.SettingsFragment; import org.droidplanner.android.helpers.LocalMapTileProvider; import org.droidplanner.android.maps.DPMap; import org.droidplanner.android.maps.MarkerInfo; import org.droidplanner.android.maps.providers.DPMapProvider; import org.droidplanner.android.utils.DroneHelper; import org.droidplanner.android.utils.collection.HashBiMap; import org.droidplanner.android.utils.prefs.AutoPanMode; import org.droidplanner.android.utils.prefs.DroidPlannerPrefs; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; public class GoogleMapFragment extends SupportMapFragment implements DPMap, LocationListener, GoogleApiClientManager.ManagerListener { private static final String TAG = GoogleMapFragment.class.getSimpleName(); public static final String PREF_MAP_TYPE = "pref_map_type"; public static final String MAP_TYPE_SATELLITE = "Satellite"; public static final String MAP_TYPE_HYBRID = "Hybrid"; public static final String MAP_TYPE_NORMAL = "Normal"; public static final String MAP_TYPE_TERRAIN = "Terrain"; // TODO: update the interval based on the user's current activity. private static final long USER_LOCATION_UPDATE_INTERVAL = 30000; // ms private static final long USER_LOCATION_UPDATE_FASTEST_INTERVAL = 1000; // ms private static final float USER_LOCATION_UPDATE_MIN_DISPLACEMENT = 0; // m public static final int REQUEST_CHECK_SETTINGS = 147; private static final float GO_TO_MY_LOCATION_ZOOM = 19f; private static final IntentFilter eventFilter = new IntentFilter(); static { eventFilter.addAction(AttributeEvent.GPS_POSITION); eventFilter.addAction(SettingsFragment.ACTION_MAP_ROTATION_PREFERENCE_UPDATED); eventFilter.addAction(SettingsFragment.ACTION_LOCATION_SETTINGS_UPDATED); } private final static Api<? extends Api.ApiOptions.NotRequiredOptions>[] apisList = new Api[]{LocationServices.API}; private final BroadcastReceiver eventReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); switch (action) { case AttributeEvent.GPS_POSITION: if (mPanMode.get() == AutoPanMode.DRONE) { final Drone drone = getDroneApi(); if (!drone.isConnected()) return; final Gps droneGps = drone.getAttribute(AttributeType.GPS); if (droneGps != null && droneGps.isValid()) { final LatLong droneLocation = droneGps.getPosition(); updateCamera(droneLocation); } } break; case SettingsFragment.ACTION_MAP_ROTATION_PREFERENCE_UPDATED: getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { setupMapUI(googleMap); } }); break; case SettingsFragment.ACTION_LOCATION_SETTINGS_UPDATED: final int resultCode = intent.getIntExtra(SettingsFragment.EXTRA_RESULT_CODE, Activity.RESULT_OK); switch (resultCode) { case Activity.RESULT_OK: // All required changes were successfully made. Try to acquire user location again mGApiClientMgr.addTask(mRequestLocationUpdateTask); break; case Activity.RESULT_CANCELED: // The user was asked to change settings, but chose not to Toast.makeText(getActivity(), "Please update your location settings!", Toast.LENGTH_LONG).show(); break; default: break; } break; } } }; private final HashBiMap<MarkerInfo, Marker> mBiMarkersMap = new HashBiMap<MarkerInfo, Marker>(); private DroidPlannerPrefs mAppPrefs; private final AtomicReference<AutoPanMode> mPanMode = new AtomicReference<AutoPanMode>( AutoPanMode.DISABLED); private final GoogleApiClientTask mGoToMyLocationTask = new GoogleApiClientTask() { @Override public void doRun() { final Location myLocation = LocationServices.FusedLocationApi.getLastLocation(getGoogleApiClient()); if (myLocation != null) { updateCamera(DroneHelper.LocationToCoord(myLocation), GO_TO_MY_LOCATION_ZOOM); if (mLocationListener != null) mLocationListener.onLocationChanged(myLocation); } } }; private final GoogleApiClientTask mRemoveLocationUpdateTask = new GoogleApiClientTask() { @Override public void doRun() { LocationServices.FusedLocationApi.removeLocationUpdates(getGoogleApiClient(), GoogleMapFragment.this); } }; private final GoogleApiClientTask mRequestLocationUpdateTask = new GoogleApiClientTask() { @Override public void doRun() { final LocationRequest locationReq = LocationRequest.create() .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) .setFastestInterval(USER_LOCATION_UPDATE_FASTEST_INTERVAL) .setInterval(USER_LOCATION_UPDATE_INTERVAL) .setSmallestDisplacement(USER_LOCATION_UPDATE_MIN_DISPLACEMENT); final GoogleApiClient googleApiClient = getGoogleApiClient(); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(locationReq); PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build()); result.setResultCallback(new ResultCallback<LocationSettingsResult>() { @Override public void onResult(LocationSettingsResult callback) { final Status status = callback.getStatus(); switch (status.getStatusCode()) { case LocationSettingsStatusCodes.SUCCESS: // All location settings are satisfied. The client can initialize location // requests here. LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationReq, GoogleMapFragment.this); break; case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: // Location settings are not satisfied. But could be fixed by showing the user // a dialog. try { // Show the dialog by calling startResolutionForResult(), // and check the result in onActivityResult(). status.startResolutionForResult(getActivity(), REQUEST_CHECK_SETTINGS); } catch (IntentSender.SendIntentException e) { // Ignore the error. Log.e(TAG, e.getMessage(), e); } break; case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: // Location settings are not satisfied. However, we have no way to fix the // settings so we won't show the dialog. Log.w(TAG, "Unable to get accurate user location."); Toast.makeText(getActivity(), "Unable to get accurate location. Please update your " + "location settings!", Toast.LENGTH_LONG).show(); break; } } }); } }; private final GoogleApiClientTask requestLastLocationTask = new GoogleApiClientTask() { @Override protected void doRun() { final Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(getGoogleApiClient()); if (lastLocation != null && mLocationListener != null) { mLocationListener.onLocationChanged(lastLocation); } } }; private GoogleApiClientManager mGApiClientMgr; private Marker userMarker; private Polyline flightPath; private Polyline missionPath; private Polyline mDroneLeashPath; private int maxFlightPathSize; /* * DP Map listeners */ private DPMap.OnMapClickListener mMapClickListener; private DPMap.OnMapLongClickListener mMapLongClickListener; private DPMap.OnMarkerClickListener mMarkerClickListener; private DPMap.OnMarkerDragListener mMarkerDragListener; private android.location.LocationListener mLocationListener; protected boolean useMarkerClickAsMapClick = false; private List<Polygon> polygonsPaths = new ArrayList<Polygon>(); protected DroidPlannerApp dpApp; private Polygon footprintPoly; @Override public void onAttach(Activity activity) { super.onAttach(activity); dpApp = (DroidPlannerApp) activity.getApplication(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) { final FragmentActivity activity = getActivity(); final Context context = activity.getApplicationContext(); final View view = super.onCreateView(inflater, viewGroup, bundle); mGApiClientMgr = new GoogleApiClientManager(context, new Handler(), apisList); mGApiClientMgr.setManagerListener(this); mAppPrefs = new DroidPlannerPrefs(context); final Bundle args = getArguments(); if (args != null) { maxFlightPathSize = args.getInt(EXTRA_MAX_FLIGHT_PATH_SIZE); } return view; } @Override public void onStart() { super.onStart(); mGApiClientMgr.start(); mGApiClientMgr.addTask(mRequestLocationUpdateTask); LocalBroadcastManager.getInstance(getActivity().getApplicationContext()) .registerReceiver(eventReceiver, eventFilter); setupMap(); } @Override public void onStop() { super.onStop(); mGApiClientMgr.addTask(mRemoveLocationUpdateTask); LocalBroadcastManager.getInstance(getActivity().getApplicationContext()) .unregisterReceiver(eventReceiver); mGApiClientMgr.stopSafely(); } @Override public void clearFlightPath() { if (flightPath != null) { List<LatLng> oldFlightPath = flightPath.getPoints(); oldFlightPath.clear(); flightPath.setPoints(oldFlightPath); } } @Override public LatLong getMapCenter() { return DroneHelper.LatLngToCoord(getMap().getCameraPosition().target); } @Override public float getMapZoomLevel() { return getMap().getCameraPosition().zoom; } @Override public float getMaxZoomLevel() { return getMap().getMaxZoomLevel(); } @Override public float getMinZoomLevel() { return getMap().getMinZoomLevel(); } @Override public void selectAutoPanMode(AutoPanMode target) { final AutoPanMode currentMode = mPanMode.get(); if (currentMode == target) return; setAutoPanMode(currentMode, target); } private Drone getDroneApi() { return dpApp.getDrone(); } private void setAutoPanMode(AutoPanMode current, AutoPanMode update) { if (mPanMode.compareAndSet(current, update)) { switch (current) { case DRONE: LocalBroadcastManager.getInstance(getActivity().getApplicationContext()) .unregisterReceiver(eventReceiver); break; case DISABLED: default: break; } switch (update) { case DRONE: LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).registerReceiver (eventReceiver, eventFilter); break; case DISABLED: default: break; } } } @Override public DPMapProvider getProvider() { return DPMapProvider.GOOGLE_MAP; } @Override public void addFlightPathPoint(LatLong coord) { final LatLng position = DroneHelper.CoordToLatLang(coord); if (maxFlightPathSize > 0) { if (flightPath == null) { PolylineOptions flightPathOptions = new PolylineOptions(); flightPathOptions.color(FLIGHT_PATH_DEFAULT_COLOR) .width(FLIGHT_PATH_DEFAULT_WIDTH).zIndex(1); flightPath = getMap().addPolyline(flightPathOptions); } List<LatLng> oldFlightPath = flightPath.getPoints(); if (oldFlightPath.size() > maxFlightPathSize) { oldFlightPath.remove(0); } oldFlightPath.add(position); flightPath.setPoints(oldFlightPath); } } @Override public void clearMarkers() { for (Marker marker : mBiMarkersMap.valueSet()) { marker.remove(); } mBiMarkersMap.clear(); } @Override public void updateMarker(MarkerInfo markerInfo) { updateMarker(markerInfo, markerInfo.isDraggable()); } @Override public void updateMarker(MarkerInfo markerInfo, boolean isDraggable) { // if the drone hasn't received a gps signal yet final LatLong coord = markerInfo.getPosition(); if (coord == null) { return; } final LatLng position = DroneHelper.CoordToLatLang(coord); Marker marker = mBiMarkersMap.getValue(markerInfo); if (marker == null) { // Generate the marker generateMarker(markerInfo, position, isDraggable); } else { // Update the marker updateMarker(marker, markerInfo, position, isDraggable); } } private void generateMarker(MarkerInfo markerInfo, LatLng position, boolean isDraggable) { final MarkerOptions markerOptions = new MarkerOptions() .position(position) .draggable(isDraggable) .alpha(markerInfo.getAlpha()) .anchor(markerInfo.getAnchorU(), markerInfo.getAnchorV()) .infoWindowAnchor(markerInfo.getInfoWindowAnchorU(), markerInfo.getInfoWindowAnchorV()).rotation(markerInfo.getRotation()) .snippet(markerInfo.getSnippet()).title(markerInfo.getTitle()) .flat(markerInfo.isFlat()).visible(markerInfo.isVisible()); final Bitmap markerIcon = markerInfo.getIcon(getResources()); if (markerIcon != null) { markerOptions.icon(BitmapDescriptorFactory.fromBitmap(markerIcon)); } Marker marker = getMap().addMarker(markerOptions); mBiMarkersMap.put(markerInfo, marker); } private void updateMarker(Marker marker, MarkerInfo markerInfo, LatLng position, boolean isDraggable) { final Bitmap markerIcon = markerInfo.getIcon(getResources()); if (markerIcon != null) { marker.setIcon(BitmapDescriptorFactory.fromBitmap(markerIcon)); } marker.setAlpha(markerInfo.getAlpha()); marker.setAnchor(markerInfo.getAnchorU(), markerInfo.getAnchorV()); marker.setInfoWindowAnchor(markerInfo.getInfoWindowAnchorU(), markerInfo.getInfoWindowAnchorV()); marker.setPosition(position); marker.setRotation(markerInfo.getRotation()); marker.setSnippet(markerInfo.getSnippet()); marker.setTitle(markerInfo.getTitle()); marker.setDraggable(isDraggable); marker.setFlat(markerInfo.isFlat()); marker.setVisible(markerInfo.isVisible()); } @Override public void updateMarkers(List<MarkerInfo> markersInfos) { for (MarkerInfo info : markersInfos) { updateMarker(info); } } @Override public void updateMarkers(List<MarkerInfo> markersInfos, boolean isDraggable) { for (MarkerInfo info : markersInfos) { updateMarker(info, isDraggable); } } @Override public Set<MarkerInfo> getMarkerInfoList() { return new HashSet<MarkerInfo>(mBiMarkersMap.keySet()); } @Override public List<LatLong> projectPathIntoMap(List<LatLong> path) { List<LatLong> coords = new ArrayList<LatLong>(); Projection projection = getMap().getProjection(); for (LatLong point : path) { LatLng coord = projection.fromScreenLocation(new Point((int) point .getLatitude(), (int) point.getLongitude())); coords.add(DroneHelper.LatLngToCoord(coord)); } return coords; } @Override public void removeMarkers(Collection<MarkerInfo> markerInfoList) { if (markerInfoList == null || markerInfoList.isEmpty()) { return; } for (MarkerInfo markerInfo : markerInfoList) { Marker marker = mBiMarkersMap.getValue(markerInfo); if (marker != null) { marker.remove(); mBiMarkersMap.removeKey(markerInfo); } } } @Override public void setMapPadding(int left, int top, int right, int bottom) { getMap().setPadding(left, top, right, bottom); } @Override public void setOnMapClickListener(OnMapClickListener listener) { mMapClickListener = listener; } @Override public void setOnMapLongClickListener(OnMapLongClickListener listener) { mMapLongClickListener = listener; } @Override public void setOnMarkerDragListener(OnMarkerDragListener listener) { mMarkerDragListener = listener; } @Override public void setOnMarkerClickListener(OnMarkerClickListener listener) { mMarkerClickListener = listener; } @Override public void setLocationListener(android.location.LocationListener receiver) { mLocationListener = receiver; //Update the listener with the last received location if (mLocationListener != null) { mGApiClientMgr.addTask(requestLastLocationTask); } } private void updateCamera(final LatLong coord) { if (coord != null) { getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { final float zoomLevel = googleMap.getCameraPosition().zoom; googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(DroneHelper.CoordToLatLang(coord), zoomLevel)); } }); } } @Override public void updateCamera(final LatLong coord, final float zoomLevel) { if (coord != null) { getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom( DroneHelper.CoordToLatLang(coord), zoomLevel)); } }); } } @Override public void updateCameraBearing(float bearing) { final CameraPosition cameraPosition = new CameraPosition(DroneHelper.CoordToLatLang (getMapCenter()), getMapZoomLevel(), 0, bearing); getMap().animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); } @Override public void updateDroneLeashPath(PathSource pathSource) { List<LatLong> pathCoords = pathSource.getPathPoints(); final List<LatLng> pathPoints = new ArrayList<LatLng>(pathCoords.size()); for (LatLong coord : pathCoords) { pathPoints.add(DroneHelper.CoordToLatLang(coord)); } if (mDroneLeashPath == null) { PolylineOptions flightPath = new PolylineOptions(); flightPath.color(DRONE_LEASH_DEFAULT_COLOR).width( DroneHelper.scaleDpToPixels(DRONE_LEASH_DEFAULT_WIDTH, getResources())); mDroneLeashPath = getMap().addPolyline(flightPath); } mDroneLeashPath.setPoints(pathPoints); } @Override public void updateMissionPath(PathSource pathSource) { List<LatLong> pathCoords = pathSource.getPathPoints(); final List<LatLng> pathPoints = new ArrayList<LatLng>(pathCoords.size()); for (LatLong coord : pathCoords) { pathPoints.add(DroneHelper.CoordToLatLang(coord)); } if (missionPath == null) { PolylineOptions pathOptions = new PolylineOptions(); pathOptions.color(MISSION_PATH_DEFAULT_COLOR).width( MISSION_PATH_DEFAULT_WIDTH); missionPath = getMap().addPolyline(pathOptions); } missionPath.setPoints(pathPoints); } @Override public void updatePolygonsPaths(List<List<LatLong>> paths) { for (Polygon poly : polygonsPaths) { poly.remove(); } for (List<LatLong> contour : paths) { PolygonOptions pathOptions = new PolygonOptions(); pathOptions.strokeColor(POLYGONS_PATH_DEFAULT_COLOR).strokeWidth( POLYGONS_PATH_DEFAULT_WIDTH); final List<LatLng> pathPoints = new ArrayList<LatLng>(contour.size()); for (LatLong coord : contour) { pathPoints.add(DroneHelper.CoordToLatLang(coord)); } pathOptions.addAll(pathPoints); polygonsPaths.add(getMap().addPolygon(pathOptions)); } } @Override public void addCameraFootprint(FootPrint footprintToBeDraw) { PolygonOptions pathOptions = new PolygonOptions(); pathOptions.strokeColor(FOOTPRINT_DEFAULT_COLOR).strokeWidth(FOOTPRINT_DEFAULT_WIDTH); pathOptions.fillColor(FOOTPRINT_FILL_COLOR); for (LatLong vertex : footprintToBeDraw.getVertexInGlobalFrame()) { pathOptions.add(DroneHelper.CoordToLatLang(vertex)); } getMap().addPolygon(pathOptions); } /** * Save the map camera state on a preference file * http://stackoverflow.com/questions * /16697891/google-maps-android-api-v2-restoring * -map-state/16698624#16698624 */ @Override public void saveCameraPosition() { CameraPosition camera = getMap().getCameraPosition(); mAppPrefs.prefs.edit() .putFloat(PREF_LAT, (float) camera.target.latitude) .putFloat(PREF_LNG, (float) camera.target.longitude) .putFloat(PREF_BEA, camera.bearing) .putFloat(PREF_TILT, camera.tilt) .putFloat(PREF_ZOOM, camera.zoom).apply(); } @Override public void loadCameraPosition() { getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { final SharedPreferences settings = mAppPrefs.prefs; final CameraPosition.Builder camera = new CameraPosition.Builder(); camera.bearing(settings.getFloat(PREF_BEA, DEFAULT_BEARING)); camera.tilt(settings.getFloat(PREF_TILT, DEFAULT_TILT)); camera.zoom(settings.getFloat(PREF_ZOOM, DEFAULT_ZOOM_LEVEL)); camera.target(new LatLng(settings.getFloat(PREF_LAT, DEFAULT_LATITUDE), settings.getFloat(PREF_LNG, DEFAULT_LONGITUDE))); googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(camera.build())); } }); } private void setupMap() { // Make sure the map is initialized MapsInitializer.initialize(getActivity().getApplicationContext()); getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { setupMapUI(googleMap); setupMapOverlay(googleMap); setupMapListeners(googleMap); } }); } @Override public void zoomToFit(List<LatLong> coords) { if (!coords.isEmpty()) { final List<LatLng> points = new ArrayList<LatLng>(); for (LatLong coord : coords) points.add(DroneHelper.CoordToLatLang(coord)); final LatLngBounds bounds = getBounds(points); getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { final Activity activity = getActivity(); if(activity == null) return; final View rootView = ((ViewGroup)activity.findViewById(android.R.id.content)).getChildAt(0); if(rootView == null) return; final int height = rootView.getHeight(); final int width = rootView.getWidth(); Log.d(TAG, String.format(Locale.US, "Screen W %d, H %d", width, height)); if(height > 0 && width > 0) { CameraUpdate animation = CameraUpdateFactory.newLatLngBounds(bounds, width, height, 100); googleMap.animateCamera(animation); } } }); } } @Override public void zoomToFitMyLocation(final List<LatLong> coords) { mGApiClientMgr.addTask(new GoogleApiClientTask() { @Override protected void doRun() { final Location myLocation = LocationServices.FusedLocationApi.getLastLocation(getGoogleApiClient()); if (myLocation != null) { final List<LatLong> updatedCoords = new ArrayList<LatLong>(coords); updatedCoords.add(DroneHelper.LocationToCoord(myLocation)); zoomToFit(updatedCoords); } else { zoomToFit(coords); } } }); } @Override public void goToMyLocation() { if (!mGApiClientMgr.addTask(mGoToMyLocationTask)) { Log.e(TAG, "Unable to add google api client task."); } } @Override public void goToDroneLocation() { Drone dpApi = getDroneApi(); if (!dpApi.isConnected()) return; Gps gps = dpApi.getAttribute(AttributeType.GPS); if (!gps.isValid()) { Toast.makeText(getActivity().getApplicationContext(), R.string.drone_no_location, Toast.LENGTH_SHORT).show(); return; } final float currentZoomLevel = getMap().getCameraPosition().zoom; final LatLong droneLocation = gps.getPosition(); updateCamera(droneLocation, (int) currentZoomLevel); } private void setupMapListeners(GoogleMap googleMap) { final GoogleMap.OnMapClickListener onMapClickListener = new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng latLng) { if (mMapClickListener != null) { mMapClickListener.onMapClick(DroneHelper.LatLngToCoord(latLng)); } } }; googleMap.setOnMapClickListener(onMapClickListener); googleMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() { @Override public void onMapLongClick(LatLng latLng) { if (mMapLongClickListener != null) { mMapLongClickListener.onMapLongClick(DroneHelper.LatLngToCoord(latLng)); } } }); googleMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() { @Override public void onMarkerDragStart(Marker marker) { if (mMarkerDragListener != null) { final MarkerInfo markerInfo = mBiMarkersMap.getKey(marker); markerInfo.setPosition(DroneHelper.LatLngToCoord(marker.getPosition())); mMarkerDragListener.onMarkerDragStart(markerInfo); } } @Override public void onMarkerDrag(Marker marker) { if (mMarkerDragListener != null) { final MarkerInfo markerInfo = mBiMarkersMap.getKey(marker); markerInfo.setPosition(DroneHelper.LatLngToCoord(marker.getPosition())); mMarkerDragListener.onMarkerDrag(markerInfo); } } @Override public void onMarkerDragEnd(Marker marker) { if (mMarkerDragListener != null) { final MarkerInfo markerInfo = mBiMarkersMap.getKey(marker); markerInfo.setPosition(DroneHelper.LatLngToCoord(marker.getPosition())); mMarkerDragListener.onMarkerDragEnd(markerInfo); } } }); googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() { @Override public boolean onMarkerClick(Marker marker) { if (useMarkerClickAsMapClick) { onMapClickListener.onMapClick(marker.getPosition()); return true; } if (mMarkerClickListener != null) { final MarkerInfo markerInfo = mBiMarkersMap.getKey(marker); if(markerInfo != null) return mMarkerClickListener.onMarkerClick(markerInfo); } return false; } }); } private void setupMapUI(GoogleMap map) { map.setMyLocationEnabled(false); UiSettings mUiSettings = map.getUiSettings(); mUiSettings.setMyLocationButtonEnabled(false); mUiSettings.setMapToolbarEnabled(false); mUiSettings.setCompassEnabled(false); mUiSettings.setTiltGesturesEnabled(false); mUiSettings.setZoomControlsEnabled(false); mUiSettings.setRotateGesturesEnabled(mAppPrefs.isMapRotationEnabled()); } private void setupMapOverlay(GoogleMap map) { if (mAppPrefs.isOfflineMapEnabled()) { setupOfflineMapOverlay(map); } else { setupOnlineMapOverlay(map); } } private void setupOnlineMapOverlay(GoogleMap map) { map.setMapType(getMapType()); } private int getMapType() { String mapType = mAppPrefs.getMapType(); if (mapType.equalsIgnoreCase(MAP_TYPE_SATELLITE)) { return GoogleMap.MAP_TYPE_SATELLITE; } if (mapType.equalsIgnoreCase(MAP_TYPE_HYBRID)) { return GoogleMap.MAP_TYPE_HYBRID; } if (mapType.equalsIgnoreCase(MAP_TYPE_NORMAL)) { return GoogleMap.MAP_TYPE_NORMAL; } if (mapType.equalsIgnoreCase(MAP_TYPE_TERRAIN)) { return GoogleMap.MAP_TYPE_TERRAIN; } else { return GoogleMap.MAP_TYPE_SATELLITE; } } private void setupOfflineMapOverlay(GoogleMap map) { map.setMapType(GoogleMap.MAP_TYPE_NONE); TileOverlay tileOverlay = map.addTileOverlay(new TileOverlayOptions() .tileProvider(new LocalMapTileProvider())); tileOverlay.setZIndex(-1); tileOverlay.clearTileCache(); } protected void clearMap() { getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { googleMap.clear(); setupMapOverlay(googleMap); } }); } private LatLngBounds getBounds(List<LatLng> pointsList) { LatLngBounds.Builder builder = new LatLngBounds.Builder(); for (LatLng point : pointsList) { builder.include(point); } return builder.build(); } public double getMapRotation() { GoogleMap map = getMap(); if (map != null) { return map.getCameraPosition().bearing; } else { return 0; } } @Override public void onLocationChanged(Location location) { //Update the user location icon. if (userMarker == null) { final MarkerOptions options = new MarkerOptions() .position(new LatLng(location.getLatitude(), location.getLongitude())) .draggable(false) .flat(true) .visible(true) .icon(BitmapDescriptorFactory.fromResource(R.drawable.blue)); getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { userMarker = googleMap.addMarker(options); } }); } else { userMarker.setPosition(new LatLng(location.getLatitude(), location.getLongitude())); } if (mPanMode.get() == AutoPanMode.USER) { Log.d(TAG, "User location changed."); updateCamera(DroneHelper.LocationToCoord(location), (int) getMap().getCameraPosition().zoom); } if (mLocationListener != null) { mLocationListener.onLocationChanged(location); } } @Override public void skipMarkerClickEvents(boolean skip) { useMarkerClickAsMapClick = skip; } @Override public void updateRealTimeFootprint(FootPrint footprint) { List<LatLong> pathPoints = footprint == null ? Collections.<LatLong>emptyList() : footprint.getVertexInGlobalFrame(); if (pathPoints.isEmpty()) { if (footprintPoly != null) { footprintPoly.remove(); footprintPoly = null; } } else { if (footprintPoly == null) { PolygonOptions pathOptions = new PolygonOptions() .strokeColor(FOOTPRINT_DEFAULT_COLOR) .strokeWidth(FOOTPRINT_DEFAULT_WIDTH) .fillColor(FOOTPRINT_FILL_COLOR); for (LatLong vertex : pathPoints) { pathOptions.add(DroneHelper.CoordToLatLang(vertex)); } footprintPoly = getMap().addPolygon(pathOptions); } else { List<LatLng> list = new ArrayList<LatLng>(); for (LatLong vertex : pathPoints) { list.add(DroneHelper.CoordToLatLang(vertex)); } footprintPoly.setPoints(list); } } } @Override public void onGoogleApiConnectionError(ConnectionResult connectionResult) { final Activity activity = getActivity(); if (activity == null) return; if (connectionResult.hasResolution()) { try { connectionResult.startResolutionForResult(activity, 0); } catch (IntentSender.SendIntentException e) { //There was an error with the resolution intent. Try again. if (mGApiClientMgr != null) mGApiClientMgr.start(); } } else { onUnavailableGooglePlayServices(connectionResult.getErrorCode()); } } @Override public void onUnavailableGooglePlayServices(int i) { final Activity activity = getActivity(); if (activity != null) { GooglePlayServicesUtil.showErrorDialogFragment(i, getActivity(), 0, new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { activity.finish(); } }); } } @Override public void onManagerStarted() { } @Override public void onManagerStopped() { } }